// vi:si:et:sw=4:sts=4:ts=4
package cat.hex.trainoid;

import java.util.ArrayList;
import java.util.Date;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;

/*
 * this class implements LocationListener, which listens for both changes in
 * the location of the device and changes in the status of the GPS system.
 */
public class GPSReceiver extends Service implements LocationListener
{
	// For identify recording track over classes
	public final static int RECORDING_TRACK_ID = -1;

    // Global minimum time between positions
    protected static long LOC_MIN_TIME = 1000;

    // Global minimum distance between positions
    protected static float LOC_MIN_DISTANCE = 10f;

    // Select the location provider
    protected static String LOC_PROVIDER = LocationManager.GPS_PROVIDER;

    static final private String tag = "GPSReceiver"; // for Log

    protected static LocationManager lm;
    protected static LocationListener myLocationListener;
    protected static Context myGPSContext;

    // passing info to the service process because is always running
    protected ArrayList<Location> path;
    protected static GPSReceiver instance;
    protected static boolean isFollowing = false;
    protected static boolean isRecording = false;
    protected Map mMap;
    protected Location myCurrentLocation;
    protected long mTrackId = -1;

    /* Functions begin */
    public static GPSReceiver getInstance()
    {
        if (instance == null)
            return new GPSReceiver();
        else
            return instance;
    }

    /*
     * not using IPC (inter-process communication)... but if we use in future
     */
    @Override
    public IBinder onBind(Intent intent)
    {
        Log.d("Service", "onBind");
        return null;
    }

    @Override
    public void onDestroy()
    {
        super.onDestroy();

        Toast.makeText(this, "Static string: GPS Service stopped", Toast.LENGTH_LONG).show();
        Log.d("Service", "onDestroy");
    }

    /** Called when the intent is requested from the pool */
    @Override
    public void onStart(Intent intent, int startid)
    {
        //Toast.makeText(this, "(tr auto) GPS Service started", Toast.LENGTH_LONG).show();
        Log.v(tag, "activity GPSReceiver started");

        if (lm == null)
            lm = (LocationManager) getSystemService(LOCATION_SERVICE);
        //lm.requestLocationUpdates(LOC_PROVIDER, LOC_MIN_TIME, LOC_MIN_DISTANCE, this);
    }

    /** Called when the intent is first created. */
    @Override
    public void onCreate()
    {
        super.onCreate();

        instance = this;

        myLocationListener = this;
        myGPSContext = this;

        if (path != null)
            path.clear();
        else
            path = new ArrayList<Location>();

        /*
         * the location manager is the most vital part it allows access to
         * location and GPS status services
         */
        lm = (LocationManager) getSystemService(LOCATION_SERVICE);
        //Toast.makeText(this, "lm created", Toast.LENGTH_LONG).show();
        Log.v(tag, "activity GPSReceiver created");
    }

    private Boolean saveLocation(Location location)
    {
    	Boolean status = true;
    	DbAdapter mDbHelper = new DbAdapter(this);
  	
    	mDbHelper.open();
    	mDbHelper.createPosition(RECORDING_TRACK_ID, location);
    	mDbHelper.close();
    	
    	return status;
    }

    public void onLocationChanged(Location location)
    {
    	Boolean ret = false;
        Log.v(tag, "Location Changed");
        myCurrentLocation = location;

        if (isRecording == true)
        {
            ret = saveLocation(location);

            debugPrintLocation(location);
        }
        else
        {
            Log.d(tag, "is recording false so don't save the position");
        }
        Log.d(tag, "****************************************       NEW  Position detected, status: " + ret);

        // call to re-draw Map function
        drawMap();
    }

    /* a simple print function */
    private void debugPrintLocation(Location location)
    {
        StringBuilder sb = new StringBuilder(512);

        sb.append("Latitude: ");
        sb.append(location.getLatitude());
        sb.append('\n');

        sb.append("Longitude: ");
        sb.append(location.getLongitude());
        sb.append('\n');

        sb.append("Altitiude: ");
        sb.append(location.getAltitude());
        sb.append('\n');

        sb.append("Accuracy: ");
        sb.append(location.getAccuracy());
        sb.append('\n');

        sb.append("Timestamp: ");
        Date timestamp = new Date(location.getTime());
        sb.append(timestamp.toLocaleString());
        sb.append('\n');

        Log.d(tag, sb.toString());
    }

    public void onProviderDisabled(String provider)
    {
        /* this is called if/when the GPS is disabled in settings */
        Log.v(tag, "trainoid Provider Disabled");

        /* bring up the GPS settings */
        Toast.makeText(this, "GPS is disabled, Trainoid is unable to detect your position", Toast.LENGTH_SHORT).show();

        //        Intent intent = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
        //        startActivity(intent);
    }

    public void onProviderEnabled(String provider)
    {
        Log.v(tag, "Enabled");
        Toast.makeText(this, "trainoid GPS Enabled", Toast.LENGTH_SHORT).show();

        if (isRecording == true)
            lm.requestLocationUpdates(LOC_PROVIDER, LOC_MIN_TIME, LOC_MIN_DISTANCE, this);
    }

    public void onStatusChanged(String provider, int status, Bundle extras)
    {
        /* This is called when the GPS status alters */
        switch (status)
        {
            case LocationProvider.OUT_OF_SERVICE:
                Log.v(tag, "Status Changed: Out of Service");
                Toast.makeText(this, "tr Status Changed: Out of Service", Toast.LENGTH_SHORT).show();
                break;
            case LocationProvider.TEMPORARILY_UNAVAILABLE:
                Log.v(tag, "Status Changed: Temporarily Unavailable");
                Toast.makeText(this, "tr Status Changed: Temporarily Unavailable", Toast.LENGTH_SHORT).show();
                break;
            case LocationProvider.AVAILABLE:
                Log.v(tag, "Status Changed: Available");
                Toast.makeText(this, "tr Status Changed: Available", Toast.LENGTH_SHORT).show();
                break;
        }
    }

    public void startRecording()
    {
        isRecording = true;
        lm.requestLocationUpdates(LOC_PROVIDER, LOC_MIN_TIME, LOC_MIN_DISTANCE, myLocationListener);

        Log.v(tag, "********** service STARTED by hand");
        Toast.makeText(myGPSContext, "Now I'm recording your track", Toast.LENGTH_SHORT).show();
    }

    public void stopRecording()
    {
        isRecording = false;
        lm.removeUpdates(myLocationListener);

        Log.v(tag, "********** service stopped by hand");
        Toast.makeText(myGPSContext, "No more positions will be recorded", Toast.LENGTH_SHORT).show();

        // maybe trainoid.saveTrack() because we wanna allow edit options (name, description) through an Edit Activity
        Track t = new Track(this);
        t.readRecordingPath(); // This line and the line below must update RECORDING_TRACK_ID into a new track id, not
        t.saveTrack();         // just copy the points and save them as a new track.

        if(!deleteTempRecording())
        {
        	Toast.makeText(myGPSContext, "Error deleting temporary record", Toast.LENGTH_SHORT).show();
        }
        //path.clear();
    }

    private Boolean deleteTempRecording()
    {
    	Boolean result = false;
       	DbAdapter mDbHelper = new DbAdapter(myGPSContext);

    	mDbHelper.open();
    	result = mDbHelper.deleteTrack(RECORDING_TRACK_ID);
    	mDbHelper.close();

    	return result;
    }

    public ArrayList<Location> getPath()
    {
    	Track t = new Track(this);
    	t.readRecordingPath();
    	return t.getPath();
    }

    public boolean isRecording()
    {
        return isRecording;
    }

    public void setFollowing(boolean status)
    {
        isFollowing = status;
    }

    public void setTrackId(long id)
    {
        mTrackId = id;
    }

    public boolean isFollowing()
    {
        return isFollowing;
    }

    public boolean isMapCreated()
    {
        return !(mMap == null);
    }

    public void setMap(Map map)
    {
        this.mMap = map;
    }

    private void drawMap()
    {
        if (mMap != null)
            mMap.refresh();
    }

    public Location getCurrentLocation()
    {
        return myCurrentLocation;
    }
}
